package de.slothsoft.commons.swing.window;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.util.HashMap;
import java.util.Map;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ComboBoxModel;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;

import de.slothsoft.commons.IDisplayable;
import de.slothsoft.commons.res.CommonMessages;
import de.slothsoft.commons.swing.JCheckList;
import de.slothsoft.commons.swing.SwingConstants;
import de.slothsoft.commons.swing.util.ControlUtil;
import de.slothsoft.commons.swing.util.WindowUtil;

public abstract class AbstractDialog extends JDialog {

	// TODO: databinding is needed in dialogs / frames / panels

	public static enum ButtonType {
		OK, CANCEL, CLOSE;
	}

	private static final long serialVersionUID = 1L;

	private ButtonType answer;
	private ButtonType[] activeButtons;
	private Map<ButtonType, JButton> buttonMap = new HashMap<ButtonType, JButton>();

	@Deprecated
	public AbstractDialog() {
		this((Window) null);
	}

	public AbstractDialog(Window parent) {
		this(parent, ButtonType.OK, ButtonType.CANCEL);
	}

	@Deprecated
	public AbstractDialog(ButtonType... buttons) {
		this(null, buttons);
	}

	public AbstractDialog(Window parent, ButtonType... buttons) {
		super(parent);
		this.activeButtons = buttons;
		if (this.activeButtons == null) {
			this.activeButtons = new ButtonType[0];
		}
		setModal(true); // dialogs are supposed to be modal
		hookCloseOnExcape();
	}

	public void open() {
		createLayout();
		internalSetVisible(true);
	}

	/**
	 * Use open() instead.
	 */

	@Override
	@Deprecated
	public void setVisible(boolean b) {
		if (b) {
			open();
		} else {
			internalSetVisible(b);
		}
	}

	protected void internalSetVisible(boolean b) {
		super.setVisible(b);
	}

	protected void createLayout() {
		setLayout(new BorderLayout());
		JPanel mainPanel = new JPanel();
		createMainPanel(mainPanel);
		add(mainPanel, BorderLayout.CENTER);
		add(createButtonPanel(), BorderLayout.SOUTH);
		pack();
		centerDialog();
	}

	protected void centerDialog() {
		if (getParent() != null) {
			WindowUtil.centerWindowOnParent(this, getParent());
		} else {
			WindowUtil.centerWindow(this);
		}
	}

	protected Component createButtonPanel() {
		JPanel parent = new JPanel();
		parent.setLayout(new BorderLayout());

		int buttonSpacing = 5;

		JPanel buttonPanel = new JPanel();
		buttonPanel
				.setLayout(new GridLayout(1, 0, buttonSpacing, buttonSpacing));
		buttonPanel.setBorder(BorderFactory.createEmptyBorder(buttonSpacing,
				buttonSpacing, buttonSpacing, buttonSpacing));
		createButtons(buttonPanel);

		parent.add(new JLabel(), BorderLayout.CENTER);
		parent.add(buttonPanel, BorderLayout.EAST);

		return parent;
	}

	protected void createButtons(JPanel buttonPanel) {
		JButton button;

		if (showOkButton()) {
			button = new JButton(CommonMessages.getString("Dialog.ok")); //$NON-NLS-1$
			button.addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					performOkAction();
				}
			});
			button.setMinimumSize(SwingConstants.BUTTON_MIN_SIZE);
			buttonPanel.add(button);
			this.buttonMap.put(ButtonType.OK, button);
		}

		if (showCancelButton()) {
			button = new JButton(CommonMessages.getString("Dialog.cancel")); //$NON-NLS-1$
			button.addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					performCancelAction();
				}
			});
			button.setMinimumSize(SwingConstants.BUTTON_MIN_SIZE);
			buttonPanel.add(button);
			this.buttonMap.put(ButtonType.CANCEL, button);
		}

		if (showCloseButton()) {
			button = new JButton(CommonMessages.getString("Dialog.close")); //$NON-NLS-1$
			button.addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					performCloseAction();
				}
			});
			button.setMinimumSize(SwingConstants.BUTTON_MIN_SIZE);
			buttonPanel.add(button);
			this.buttonMap.put(ButtonType.CLOSE, button);
		}
	}

	protected JButton getButton(ButtonType type) {
		return this.buttonMap.get(type);
	}

	protected boolean showOkButton() {
		return showButton(ButtonType.OK);
	}

	protected boolean showCancelButton() {
		return showButton(ButtonType.CANCEL);
	}

	protected boolean showCloseButton() {
		return showButton(ButtonType.CLOSE);
	}

	private boolean showButton(ButtonType searchedType) {
		for (ButtonType type : this.activeButtons) {
			if (type == searchedType) {
				return true;
			}
		}
		return false;
	}

	protected void performOkAction() {
		// to be overridden
		this.answer = ButtonType.OK;
	}

	protected void performCancelAction() {
		this.answer = ButtonType.CANCEL;
		close();
	}

	protected void performCloseAction() {
		this.answer = ButtonType.CLOSE;
		close();
	}

	public ButtonType getAnswer() {
		return this.answer;
	}

	protected void close() {
		setVisible(false);
		dispose();
	}

	private void hookCloseOnExcape() {
		String key = "WINDOW_CLOSING";
		KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);

		Action closing = new AbstractAction() {
			private static final long serialVersionUID = 1L;

			@Override
			public void actionPerformed(ActionEvent event) {
				dispatchEvent(new WindowEvent(AbstractDialog.this,
						WindowEvent.WINDOW_CLOSING));
			}
		};
		getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
				escape, key);
		getRootPane().getActionMap().put(key, closing);
	}

	/*
	 * helper methods
	 */

	protected JLabel createLabel(JComponent component, String text, int x, int y) {
		return ControlUtil.createLabel(component, text, x, y);
	}

	protected JLabel createLabel(JComponent component, Icon icon, int x, int y) {
		return ControlUtil.createLabel(component, icon, x, y);
	}

	protected JTextField createText(JComponent component, int x, int y) {
		return ControlUtil.createText(component, x, y);
	}

	protected JTextArea createTextArea(JComponent component, int x, int y) {
		return ControlUtil.createTextArea(component, x, y);
	}

	protected JCheckBox createCheckBox(JComponent component, int x, int y) {
		return ControlUtil.createCheckBox(component, x, y);
	}

	protected <T> JComboBox<T> createCombo(JComponent parent,
			ComboBoxModel<T> model, int x, int y) {
		return ControlUtil.createCombo(parent, model, x, y);
	}

	protected <T extends IDisplayable> JCheckList<T> createCheckList(
			JComponent parent, T[] elements, int x, int y) {
		return ControlUtil.createCheckList(parent, elements, x, y);
	}

	/*
	 * Abstract Methods
	 */

	protected abstract void createMainPanel(JPanel mainPanel);

}
